from django.utils.deprecation import MiddlewareMixin
import re
from rest_framework_jwt.utils import jwt_decode_handler
from jwt.exceptions import ExpiredSignatureError,DecodeError
from django.http import JsonResponse
import redis
import json
#RBAC鉴权中间件
class MyRBAC(MiddlewareMixin):
    #处理请求
    def process_request(self,request):
        #1 . 获取用户访问的地址、请求方法
        path = request.path_info[1:]
        method = request.method.lower()
        #2.判断是否 登录 或者 管理员的请求
        if re.findall(r"login|admin",path,re.I):
            #继续往后走， 放过该请求
            return None
        # 3 .用户后续的请求，需要权限校验
        #判断用户是否登录
        payload = self.validate_token(request)
        if  payload:
            #用户已登录， 权限校验
            redis_conn = redis.Redis()
            print("解析出payload数据:",payload)
            #构造key,用来取用户的所有权限
            key = "user_permissions_%s"%payload.get('user_id')
            permissions = redis_conn.get(key)
            #获取该用户的权限列表 如'["get ^user/permission/$",....]'
            permission_list = json.loads(permissions.decode())
            #判断当前请求的地址，在不在权限列表
            for p in permission_list:
                print("...",p)
                #先比较请求的方法
                if p.startswith(method):
                    #比较 url地址
                    re_exp = p.split()[1]
                    if re.findall(re_exp,path,re.I):
                        # 具有权限，请求继续向后走
                        return None
            #无权限访问
            return JsonResponse({'code':401,'msg':'无权限访问'},status=200)



        else:
            #token验证失败， 用户没有登录
            return JsonResponse({'code':403,'msg':'用户没有登录'},status=200)

    @staticmethod
    def validate_token(request):
        #获取token
        token = request.headers.get('Authorization')
        # 没有token
        if  not token:
            return False
        #有token,则解析
        token = token.split()[1]
        try:
            payload = jwt_decode_handler(token)
            return payload
        except ExpiredSignatureError:
            return False
        except DecodeError:
            return False
